Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PayerRegistry implementation #3

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open

PayerRegistry implementation #3

wants to merge 7 commits into from

Conversation

fbac
Copy link
Collaborator

@fbac fbac commented Mar 18, 2025

Closes #25

Due to the PR size, unit testing will be added in an upcoming PR.

Summary by CodeRabbit

  • Chores

    • Updated .gitignore to include the .wake/ directory.
    • Updated configuration settings in .solhint.json to disable the custom errors rule.
  • New Features

    • Introduced the PayerRegistry smart contract for managing payer USDC deposits, usage settlements, and secure withdrawals.
    • Added new interfaces for IFeeDistributor, IPayerRegistry, and IPayerReportManager, enhancing the framework for reward distribution and payer management.
    • Implemented various functions for registering payers, managing deposits and withdrawals, and handling usage settlements.

Copy link

coderabbitai bot commented Mar 18, 2025

Walkthrough

The pull request introduces updates to configuration files, updates subproject commits, and adds several new files. The changes include a .gitignore entry and a .solhint.json rule change. New smart contract functionality is added via the PayerRegistry.sol contract, and three new interface files (IFeeDistributor, IPayerRegistry, and IPayerReportManager) are created to define standardized behaviors for fee distribution, payer registry management, and report management.

Changes

Files Change Summary
.gitignore Added new entry .wake/ to ignore the .wake directory.
.solhint.json Added a new rule "custom-errors": "off".
lib/forge-std, lib/oz, lib/oz-upgradeable Updated subproject commit hashes to new versions.
src/PayerRegistry.sol Introduced a new smart contract for managing payer registrations, deposits, withdrawals, and settlements with various administrative and security functions.
src/interfaces/IFeeDistributor.sol, src/interfaces/IPayerRegistry.sol, src/interfaces/IPayerReportManager.sol Added new interface definitions for fee distribution, payer registry operations, and payer report management with associated events, errors, structs, and methods.

Sequence Diagram(s)

sequenceDiagram
    participant Payer as Payer
    participant Registry as PayerRegistry
    participant Admin as Administrator

    Payer->>Registry: register(amount)
    Registry-->>Payer: Emit Registration Event

    Payer->>Registry: deposit(amount)
    Registry-->>Payer: Emit Deposit Event

    Payer->>Registry: requestWithdrawal(amount)
    Registry-->>Payer: Emit Withdrawal Request Event

    Admin->>Registry: settleUsage(originatorNode, payerList, amounts)
    Registry-->>Admin: Update balances and settle fees
Loading
sequenceDiagram
    participant Node as Originator Node
    participant ReportMgr as PayerReportManager

    Node->>ReportMgr: submitPayerReport(report)
    ReportMgr-->>Node: Emit PayerReportSubmitted

    Node->>ReportMgr: attestPayerReport(reportIndex)
    ReportMgr-->>Node: Emit PayerReportAttested

    Node->>ReportMgr: settleUsageBatch(reportIndex, offset, payers, amounts, proof)
    ReportMgr-->>Node: Process settlement and emit events
Loading

Assessment against linked issues

Objective Addressed Explanation
Payer contract: implementation (#501)

Poem

I'm a rabbit with a hop so bright,
Coding changes danced in the moonlight.
Smart contracts and rules anew,
Interfaces fresh — oh what a view!
With every line, I rejoice and rap,
For these changes make my code a happy trap!
🐇✨


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@fbac fbac force-pushed the 03-18-payer_contract branch from 987f910 to 0f6c4e7 Compare March 18, 2025 21:50
Copy link

github-actions bot commented Mar 18, 2025

Changes to gas cost

Generated at commit: 9ad5be1a77e1f02d858d5e59a5c035178122119f, compared to commit: 7231951a06770e9909fceb812f472fc9dde96261

🧾 Summary (20% most significant diffs)

Contract Method Avg (+/-) %

Full diff report 👇
Contract Deployment Cost (+/-) Method Min (+/-) % Avg (+/-) % Median (+/-) % Max (+/-) % # Calls (+/-)
GroupMessageBroadcasterHarness 1,581,353 (-19,212) __getSequenceId
addMessage
initialize
354 (0)
2,645 (0)
2,819 (+61)
0.00%
0.00%
+2.21%
687 (-67)
8,446,410 (+2,924,012)
88,119 (-1,928)
-8.89%
+52.95%
-2.14%
354 (0)
7,073 (0)
93,901 (-2,081)
0.00%
0.00%
-2.17%
2,354 (0)
67,930,969 (0)
93,901 (-2,081)
0.00%
0.00%
-2.17%
6 (+1)
15 (0)
28 (0)
ERC1967Proxy 226,314 (-2,081) fallback 695 (0) 0.00% 502,298 (+52,613) +11.70% 36,792 (0) 0.00% 169,212,871 (0) 0.00% 2,534 (+1)
IdentityUpdateBroadcasterHarness 1,581,353 (-19,212) __setSequenceId
addIdentityUpdate
initialize
2,551 (0)
2,623 (0)
2,819 (+61)
0.00%
0.00%
+2.21%
20,641 (+1,809)
5,216,860 (-316,919)
88,119 (-1,928)
+9.61%
-5.73%
-2.14%
22,451 (0)
7,051 (0)
93,901 (-2,081)
0.00%
0.00%
-2.17%
22,451 (0)
67,930,947 (0)
93,901 (-2,081)
0.00%
0.00%
-2.17%
11 (0)
15 (0)
28 (0)
RateRegistryHarness 1,867,087 (-19,208) initialize 23,110 (+61) +0.26% 75,790 (-1,974) -2.54% 78,563 (-2,081) -2.58% 78,563 (-2,081) -2.58% 20 (0)

Copy link

github-actions bot commented Mar 18, 2025

LCOV of commit d9eb59a during Solidity xmtp/xmtpd#26

Summary coverage rate:
  lines......: 47.6% (245 of 515 lines)
  functions..: 55.3% (63 of 114 functions)
  branches...: 42.1% (48 of 114 branches)

Files changed coverage rate:
                                   |Lines       |Functions  |Branches    
  Filename                         |Rate     Num|Rate    Num|Rate     Num
  =======================================================================
  src/PayerRegistry.sol            |    -      0|    -     0|    -      0

⛔ The code coverage is too low: 47.57. Expected at least 95.

@fbac fbac force-pushed the 03-18-payer_contract branch from 22033ef to d941838 Compare March 19, 2025 11:20
@fbac fbac marked this pull request as ready for review March 19, 2025 13:40
@fbac fbac requested review from deluca-mike and neekolas March 19, 2025 13:40
@fbac fbac force-pushed the 03-18-payer_contract branch from 91fbd3f to e841d52 Compare March 19, 2025 13:43
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🧹 Nitpick comments (15)
src/interfaces/IFeeDistributor.sol (1)

38-39: Consider adding event declarations.
The documentation references events like NodesContractUpdated, PayerContractUpdated, RebatesPercentageUpdated, and UsdcTokenUpdated, but they are not defined in this interface. Defining them here would allow external tools and third-party integrators to rely on a fully specified interface.

Also applies to: 46-47, 60-61, 68-69

src/interfaces/IPayerReportManager.sol (1)

143-144: Mismatch between documentation and declared events.
The documentation for settleUsageBatch states "Emits a UsageSettled event", but the code only declares PayerReportPartiallySettled or PayerReportFullySettled. Consider updating the documentation or adding the missing event to avoid confusion.

src/PayerRegistry.sol (3)

108-110: Consider maximum array bounds for usage settlement.
In settleUsage, there is no explicit upper limit on the length of payerList. Large arrays in a single transaction may exceed gas limits. A chunking approach or batch processing could be safer.


405-414: Enforcing minimum deposit amounts to be strictly greater than defaults.
This is a strict policy. If there's a legitimate need to lower these values in the future, the contract won't permit it. You may want to allow administrators to set it equal to or less than the default if system requirements change.


607-618: Deposit validation prevents depositing while in withdrawal.
Currently, _validateAndProcessDeposit reverts if there's an active withdrawal request. If there is no policy reason to forbid depositing during withdrawal, consider relaxing or clarifying this constraint.

src/interfaces/IPayerRegistry.sol (10)

65-66: Correct the reference to reflect 'PayerRegistry' instead of 'Payer contract'.

The doc block states “Interface for errors emitted by the Payer contract”, but everything in this file pertains to the PayerRegistry.

A quick fix could be:

- * @notice Interface for errors emitted by the Payer contract.
+ * @notice Interface for errors emitted by the PayerRegistry contract.

234-243: Fix doc mismatch for the withdrawal event name.

The doc says “Emits WithdrawalRequest” (line 240), but the actual event name declared (line 60) is WithdrawalRequested.

Proposed fix in the doc:

- * Emits `WithdrawalRequest`.
+ * Emits `WithdrawalRequested`.

301-308: Synchronize the emitted event name for setting the fee distributor.

The doc indicates “Emits FeeDistributorUpdated” (line 305), but the declared event (line 12) is FeeDistributorSet.

Proposed fix in the doc:

- * Emits `FeeDistributorUpdated`.
+ * Emits `FeeDistributorSet`.

309-316: Synchronize the emitted event name for setting the payer report manager.

The doc indicates “Emits PayerReportManagerUpdated” (line 313), but the declared event (line 36) is PayerReportManagerSet.

Proposed fix in the doc:

- * Emits `PayerReportManagerUpdated`.
+ * Emits `PayerReportManagerSet`.

317-324: Synchronize the emitted event name for setting the node registry.

The doc indicates “Emits NodeRegistryUpdated” (line 321), but the declared event (line 24) is NodeRegistrySet.

Proposed fix in the doc:

- * Emits `NodeRegistryUpdated`.
+ * Emits `NodeRegistrySet`.

325-332: Synchronize the emitted event name for setting the USDC token address.

The doc indicates “Emits UsdcTokenUpdated” (line 329), but the declared event (line 48) is UsdcTokenSet.

Proposed fix in the doc:

- * Emits `UsdcTokenUpdated`.
+ * Emits `UsdcTokenSet`.

337-338: Synchronize the emitted event name for updating the minimum deposit.

The doc says “Emits MinimumDepositUpdated” (line 337), but the declared event (line 18) is MinimumDepositSet.

Proposed fix in the doc:

- * Emits `MinimumDepositUpdated`.
+ * Emits `MinimumDepositSet`.

342-348: Synchronize the naming and doc for minimum registration amount.

The doc says “Sets the minimum deposit amount required for registration” (line 342), while the parameter is described as newMinimumRegistrationAmount (line 343). Also, the doc states “Emits MinimumRegistrationAmountUpdated” (line 345), but the actual event is MinimumRegistrationAmountSet (line 21).

Proposed fix in the doc:

- * @notice Sets the minimum deposit amount required for registration.
+ * @notice Sets the minimum registration amount required to become a payer.
...
- * Emits `MinimumRegistrationAmountUpdated`.
+ * Emits `MinimumRegistrationAmountSet`.

350-356: Synchronize the emitted event name for updating the withdrawal lock period.

The doc says “Emits WithdrawalLockPeriodUpdated” (line 353), but the declared event (line 57) is WithdrawalLockPeriodSet.

Proposed fix:

- * Emits `WithdrawalLockPeriodUpdated`.
+ * Emits `WithdrawalLockPeriodSet`.

358-364: Synchronize the emitted event name for updating the transfer fees period.

The doc says “Emits TransferFeesPeriodUpdated” (line 361), but the declared event (line 39) is TransferFeesPeriodSet.

Proposed fix:

- * Emits `TransferFeesPeriodUpdated`.
+ * Emits `TransferFeesPeriodSet`.
🛑 Comments failed to post (1)
src/PayerRegistry.sol (1)

176-194: 🛠️ Refactor suggestion

Withdrawal logic is structured well.
The contract correctly locks withdrawal amounts and enforces a cooldown; however, you might wish to allow depositing while a withdrawal is pending (if that aligns with your business logic) or explicitly document why no deposits are allowed during the withdrawal lock.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (6)
src/PayerRegistry.sol (4)

395-411: Evaluate strict requirement for new minimum deposit/registration amounts.

Currently, newMinimumDeposit and newMinimumRegistrationAmount must strictly exceed default constants (_DEFAULT_MINIMUM_DEPOSIT_AMOUNT_MICRO_DOLLARS / _DEFAULT_MINIMUM_REGISTRATION_AMOUNT_MICRO_DOLLARS). This could reduce flexibility if the admin wants to lower these limits or set them to exactly the default in the future. Consider relaxing the requirement or making it configurable to allow equality or lower values if needed.

Also applies to: 416-428


610-613: Enforce more explicit decimal handling.

You treat USDC amounts as microdollars (e.g., 10,000,000 = $10). While this is workable, consider adding comments or inline clarifications in _validateAndProcessDeposit (and elsewhere) confirming the decimal relationship between "micro_dollars" and USDC (6 decimals). This helps future maintainers avoid confusion about deposit amounts and rounding behavior.


604-615: Improve naming for clarity.

_validateAndProcessDeposit is used for both self-deposit and third-party deposit, but the method name doesn’t clearly convey the difference between these scenarios. Consider renaming or adding more explicit in-code documentation about how it handles external donors vs. payer self-deposits.


655-657: Offer test coverage for negative and edge-case balances.

Given that payers can accumulate negative balances (and remain active unless fully negative), add or plan for thorough testing around:

  1. Very large usage amounts pushing balances far negative.
  2. Edge-case scenarios for withdrawal with existing debt.

Would you like me to prepare a set of test stubs that cover overflow edge cases, negative balance transitions, and partial debt settlement?

Also applies to: 672-674

src/interfaces/IPayerRegistry.sol (2)

240-241: Update documentation to match event names.

The docstring references events like WithdrawalRequest while the contract emits WithdrawalRequested. Similarly, it mentions WithdrawalFinalized but keep an eye on consistency in doc comments across the interface and the implementation contract to avoid confusion.

Also applies to: 260-261


342-346: Clarify docstring for minimum registration vs. deposit.

Both setMinimumDeposit and setMinimumRegistrationAmount mention “... required for registration” in the doc comments. Consider removing references to registration in setMinimumDeposit or clarifying that the deposit minimum is also used for subsequent deposits, not just initial registration.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91fbd3f and e841d52.

📒 Files selected for processing (2)
  • src/PayerRegistry.sol (1 hunks)
  • src/interfaces/IPayerRegistry.sol (1 hunks)
🔇 Additional comments (2)
src/PayerRegistry.sol (1)

1-800: Well-structured contract with clear event definitions and role-based checks.

Overall, the contract is solid:

  • Role restrictions (admin, payerReport) are properly enforced.
  • Zero-address checks in initialize and similar entry points effectively prevent misconfiguration.
  • Enumeration sets for active/debt payers add clarity.

No concerns regarding fundamental architecture beyond the noted suggestions.

src/interfaces/IPayerRegistry.sol (1)

1-477: Interface extraction and modular design approved.

Extracting events, errors, and logic into separate interfaces (like IFeeDistributor, IPayerReportManager, etc.) follows best practices for maintainability and code clarity. It decouples contract implementations and fosters a clean, modular design.

@fbac fbac requested a review from a team March 19, 2025 13:51
Copy link

macroscopeapp bot commented Mar 20, 2025

Implement PayerRegistry with Fee Distribution

Added PayerRegistry contract with payer management, balance tracking, withdrawal and fee distribution functionality. Includes interfaces for IFeeDistributor, IPayerRegistry, and IPayerReportManager. Updated git configuration and dependency versions.

📍Where to Start

The main implementation in PayerRegistry.sol which contains the core contract logic.


Macroscope summarized d9eb59a.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/PayerRegistry.sol (1)

228-230: ⚠️ Potential issue

Potential overflow/underflow for very large usage values.
As previously noted in older reviews, subtracting uint64 values from an int64 balance can overflow if usage is extremely large (beyond int64 range). This contract does not appear to enforce a maximum usage limit. Consider capping usage or performing explicit range checks to avoid accidental overflow.

Below is an example check to ensure usage fits within int64 bounds:

 for (uint256 i = 0; i < payerList.length; i++) {
     ...
+    require(usageAmountsList[i] <= type(int64).max, "Usage exceeds int64 limit");
     if (!_payerExists(payerList[i])) continue;
     ...
 }

Also applies to: 278-281, 292-294

🧹 Nitpick comments (2)
src/PayerRegistry.sol (2)

501-505: Avoid reverting in getIsActivePayer when the payer doesn’t exist.
Currently, this function reverts if a payer is missing. In typical scenarios, a query like “is this account active?” would safely return false for unknown payers, rather than throwing an error. Relaxing the revert to a boolean false may improve developer UX and integration ergonomics.


603-614: Consider emitting a dedicated deposit event for clarity.
_validateAndProcessDeposit updates the payer’s balance and emits a generic PayerBalanceUpdated event. A more explicit Deposit event can help index deposit-specific data in off-chain indexers and analytics, resulting in clearer on-chain bookkeeping.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e841d52 and d9eb59a.

📒 Files selected for processing (1)
  • src/PayerRegistry.sol (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Code Coverage

Comment on lines +134 to +141
function register(uint64 amount) external whenNotPaused {
PayerStorage storage $ = _getPayerStorage();

require(amount >= $.minimumRegistrationAmountMicroDollars, InsufficientAmount());

if (_payerExists(msg.sender)) revert PayerAlreadyRegistered();

IERC20($.usdcToken).safeTransferFrom(msg.sender, address(this), amount);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider marking register as nonReentrant for consistency and enhanced security.
The register(uint64 amount) function includes an external call to IERC20.safeTransferFrom, which may introduce a reentrancy vector similar to other deposit and withdrawal methods. Using the nonReentrant modifier here, as done elsewhere in the contract, helps ensure uniform protection against reentrancy.

You may apply this diff to standardize usage of the nonReentrant modifier:

-function register(uint64 amount) external whenNotPaused {
+function register(uint64 amount) external whenNotPaused nonReentrant {
    PayerStorage storage $ = _getPayerStorage();
    ...
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function register(uint64 amount) external whenNotPaused {
PayerStorage storage $ = _getPayerStorage();
require(amount >= $.minimumRegistrationAmountMicroDollars, InsufficientAmount());
if (_payerExists(msg.sender)) revert PayerAlreadyRegistered();
IERC20($.usdcToken).safeTransferFrom(msg.sender, address(this), amount);
function register(uint64 amount) external whenNotPaused nonReentrant {
PayerStorage storage $ = _getPayerStorage();
require(amount >= $.minimumRegistrationAmountMicroDollars, InsufficientAmount());
if (_payerExists(msg.sender)) revert PayerAlreadyRegistered();
IERC20($.usdcToken).safeTransferFrom(msg.sender, address(this), amount);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Payer contract: implementation
1 participant